import { IAssemblyDocTreeNode, Matrix3x4FlatData } from '@amcax/base';
import { PickerAssembly, convertMatrixBack } from '@amcax/renderer';
import { ctx2, userEvents } from '@app-cad/module';
import { AssemblyPreview } from '@app-cad-env/product/types/preview-types';
import { PRODUCT_EVENT } from '@app-cad/common/Constants';
import { calculateDeltaMatrix } from '../camera';
import { MessageTip } from '../MessageTip';
import { AssemblyNodePreview } from './AssemblyNodePreview';

type Position = { x: number; y: number };

userEvents.on(PRODUCT_EVENT.SMART_MOVING_RENDERED, () => {
  AssemblySmartMover.smartMoving = false;
});

export class AssemblySmartMover {
  static smartMoving = false;

  private smartMoveTimer = null;

  private readonly pressTime = 300; // 按下 300ms 后触发智能移动
  private readonly distanceThreshold = 10; // 智能移动距离阈值，按下等待期间不得移动超过此值，单位像素
  private target: IAssemblyDocTreeNode = null;
  private started = false;

  private animationFrameId: number | null = null;

  private startPos: Position | null = null;
  private endPos: Position | null = null;
  private moved = false; // 是否确实拖拽移动了，若否则不执行移动命令
  private deltaMatrix: Matrix3x4FlatData = null;

  private readonly assemblyNodePreview = new AssemblyNodePreview(this.picker);

  private handleMoveError = (error: any) => {
    this.reset(true);

    MessageTip.error(`智能移动失败 - ${error.message}（Code ${error.code}）`);

    // 仅当命令异常时才直接设置 AssemblySmartMover.smartMoving 为 false，
    // 正常移动完成后由 loadRefDoc 在渲染完成之后通过
    // PRODUCT_EVENT.SMART_MOVING_RENDERED 事件触发重置
    AssemblySmartMover.smartMoving = false;
  };

  constructor(private readonly picker?: PickerAssembly) {}

  private reset(restoreOriginalNode: boolean) {
    MessageTip.destroy();

    this.started = false;
    this.target = null;
    this.startPos = null;
    this.endPos = null;
    this.moved = false;
    this.deltaMatrix = null;

    this.assemblyNodePreview.destroy(restoreOriginalNode);
  }

  prepareMove(target: IAssemblyDocTreeNode, position: Position) {
    // 记录按下坐标
    this.startPos = position;
    this.endPos = position;
    // 检测长按以执行智能移动（300ms）
    this.smartMoveTimer = setTimeout(() => {
      // 判断位移是否小于阈值
      if (this.startPos && this.endPos) {
        const dx = this.endPos.x - this.startPos.x;
        const dy = this.endPos.y - this.startPos.y;
        const dist = Math.sqrt(dx * dx + dy * dy);
        if (dist > this.distanceThreshold) {
          return;
        }

        if (target) {
          this.target = target;
          this.started = true;

          AssemblySmartMover.smartMoving = true;

          MessageTip.info('进入智能移动模式，请拖动鼠标移动零件。');
        }
      }
    }, this.pressTime);
  }

  move(position: Position) {
    this.endPos = position;

    if (this.started && this.target) {
      if (!this.moved) {
        this.picker.clickNull();

        this.moved = true;
      }

      if (this.animationFrameId === null) {
        this.animationFrameId = requestAnimationFrame(() => {
          this.deltaMatrix = convertMatrixBack(
            calculateDeltaMatrix(
              this.startPos,
              this.endPos,
              this.target.matrix4Array[0],
            ),
          );
          ctx2.commandCenters.assemblyCommands
            .transform(this.target.key, this.deltaMatrix, true)
            .then((asmPreviews: AssemblyPreview[]) => {
              this.assemblyNodePreview.preview(asmPreviews);
            })
            .catch(this.handleMoveError)
            .finally(() => {
              this.animationFrameId = null; // 重置 animationFrameId
            });
        });
      }
    }
  }

  finishMove() {
    if (this.smartMoveTimer) {
      clearTimeout(this.smartMoveTimer);
      this.smartMoveTimer = null;
    }

    if (this.started) {
      if (this.target && this.moved) {
        ctx2.commandCenters.assemblyCommands
          .transform(this.target.key, this.deltaMatrix)
          .then(() => {
            this.reset(false);
          })
          .catch(this.handleMoveError);
      }
    }
  }
}
